grammar plp2;

@header {
	import java.lang.String;
	import java.util.ArrayList;
}

@rulecatch {
	catch (RecognitionException e) {
		reportError(e);
		System.exit(1);
	}
}
@members {
	private TablaSimbolos tauleta = new TablaSimbolos(null);
	
	public boolean mainTrobat=false;
	public clase tb;
	
	public String ponBlancos(int num) {
	        String cad = "";
	        for (int i = 0; i < num * 4; i++) {
	            cad += " ";
       	 	}
        	return cad;
    	}
    	private void ErrorSem5(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 5 ("+fila+","+columna+"): '"+ disfunka +"' ya existe en este ambito");
		System.exit(5);
	}	
	
	private void ErrorSem6(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 6 ("+fila+","+columna+"): '"+ disfunka +"' no ha sido declarado");
		System.exit(5);
	}
	
	private void ErrorSem7(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 7 ("+fila+","+columna+"): '"+ disfunka +"' no es una variable");
		System.exit(5);
	}
	
	private void ErrorSem8(int fila, int columna, String disfunka)
	{
		columna++;
		System.err.println("Error 8 ("+fila+","+columna+"): '"+ disfunka +"' no es un metodo");
		System.exit(5);
	}
	private void ErrorSem9()
	{	
		System.err.println("Error 9: 'main' no esta definido");
		System.exit(5);
	}
	
		
	public void emitErrorMessage(String mensaje)
	{
		System.err.println(mensaje);
		System.exit(1);
	}

	
}

@lexer::members {
	public void emitErrorMessage(String mensaje)
	{
		System.err.println(mensaje);
		System.exit(1);
	}
}


s returns [String eixida]:
	{
		$eixida = "";
	}
	(c	{$eixida += $c.eixida;	})*
	EOF
	{
		if (!mainTrobat)
		{
			ErrorSem9();
		}
	}
	;


c returns [String eixida]:
	CLASS ID
	{
		
		if (tauleta.existe($ID.text)) 
		{
			String texto=$ID.text;
			int linea=$ID.line;
			int col= $ID.pos;
			ErrorSem5(linea,col,texto);
		}
		
		tauleta.nuevoSimbolo($ID.text, 3, 0);	
	}
	LLAVEI
	{
		
		tauleta = new TablaSimbolos(tauleta);
		tb = new clase();
		tb.setCabeza("class " + $ID.text + " {\n");		
	}
	d[tb] 
	LLAVED
	{
		
		
		tb.usa(tauleta);
		
		
		tauleta = tauleta.pop();
		tb.setFi(ponBlancos(tauleta.getNum()) + "}\n");
		
		$eixida = tb.toString();		
	}
	;
	
d[clase tb]:
	(
	v {
		String uno = (String)$v.par.get(1);
		String dos = (String)$v.par.get(0);
		$tb.incorporaVariable(uno, dos, tauleta.getNum());}
	| 
	m[$tb]
	)*
	;
	

v returns [ArrayList par]
	@init
	{
	       	String vv = ponBlancos(tauleta.getNum());
        	int tipo =0;
	}:
	{
		$par = new ArrayList();
	}
	(INT 	{tipo=1;	vv+="int ";}	
	|	
	DOUBLE 	{tipo=2;	vv+="double ";})
	ID 
	{
		if (tauleta.existe($ID.text))
		{

		    String texto=$ID.text;
			int linea=$ID.line;
			int col= $ID.pos;
			ErrorSem5(linea,col,texto);
		}
		par.add($ID.text);
                vv += $ID.text + ";";
                tauleta.nuevoSimbolo($ID.text, 1, tipo);
	}
	PYC
	{
		par.add(vv + "\n");
	}
	;
	
m[clase tb]
	@init
	{
	
		String  gaviota = "";
        nodoModulo entrepa;
        	
	}:
	VOID 
	(
	MAIN
	{
		if (tauleta.existe($MAIN.text))
		{			 
		    String texto=$MAIN.text;
			int linea=$MAIN.line;
			int col= $MAIN.pos;
			ErrorSem5(linea,col,texto); 
		}
		else
		{
			tauleta.nuevoSimbolo($MAIN.text, 2, 4 );
            mainTrobat = true;
            gaviota = $MAIN.text;
		}
	}
	|
	ID
	{
		if (tauleta.existe($ID.text))
		{			 
		    String texto=$ID.text;
			int linea=$ID.line;
			int col= $ID.pos;
			ErrorSem5(linea,col,texto);
		}
		else
		{
			tauleta.nuevoSimbolo($ID.text, 2, 3 );
			gaviota = $ID.text;
        }    
	}
	)
	PARI PARD LLAVEI
	{
		entrepa = new nodoModulo(tauleta.getNum(), gaviota, ponBlancos(tauleta.getNum()) + "void " + gaviota + " () {\n");
        tauleta = new TablaSimbolos(tauleta);
	}
	decl[entrepa.variables]
	{	        
        	entrepa.asignaciones = new ArrayList();
        }        
        cuerpo[entrepa.asignaciones]
        LLAVED
        {
        	entrepa.usa(tauleta);   
        	tauleta = tauleta.pop();
        	entrepa.setFi(ponBlancos(tauleta.getNum()) + "}\n");

        	$tb.modulos.modulos.add(entrepa);
	}
	;

decl[listaVariables listaV]
	@init
	{
	        ArrayList par0 = null;
	}:	
	(v {
		par0 = $v.par;
		$listaV.mete((String)par0.get(0), (String)par0.get(1), tauleta.getNum()); })*
	;

cuerpo[ArrayList asigS]:
	(instr	{asigS.add(new asignacion($instr.eixida));})*
	;

instr returns [String eixida]
	@init
	{
		Simbolo sim;
	}:
	{
		eixida = ponBlancos(tauleta.getNum());
	}
	ID
	{
		String texto=$ID.text;
		int linea=$ID.line;
		int col=$ID.pos;
		sim = tauleta.busca(texto);
		if (sim!=null)
		{
			eixida+=$ID.text;	
		}
		else
		{
			
			ErrorSem6(linea,col,texto);
		}
		
	}
	asig[linea, col , texto]
	{
		eixida+=$asig.eixida;
		tauleta.busca($ID.text).setEmprat();
		eixida+=";\n";
	}
	PYC
	;

asig[int linea, int column, String texto] returns[String eixida]
	@init
	{
		Simbolo sim = tauleta.busca(texto);
	}:
	{
		eixida = "";
	}
	(
	PARI 
		{
		if (sim!=null)
		{
		   if (sim.esMETODO()) {                  
                   eixida = " ()";
                   } else {
                   	
						ErrorSem8(linea,column,texto);
                   }
		}
		else
		{
			
			ErrorSem6(linea,column,texto);
		}
		}
	PARD
	|
	ASIG factor 
	{ 
		if (sim!=null)
		{
		   if (sim.esVAR()) 
		   {		                     
                       eixida = " = " + $factor.eixida;
                   } 
                   else 
                   {
                        
						
						ErrorSem7(linea,column,texto);
                   }
		}
		else
		{
			
			ErrorSem6(linea,column,texto);
		}	
	}
	)
	;
	
factor returns [String eixida] 
	@init
	{
		Simbolo sim;
		String texto;
		int linea,col;
	}
	:
	{
		eixida = "";
	}
	(ENTERO
	{
		eixida = $ENTERO.text;	
	}
	|
	REAL
	{
		eixida = $REAL.text;
	}
	| 
	ID
	{
		sim = tauleta.busca($ID.text);
		if (sim == null) 
		{
 			
		    texto=$ID.text;
			linea=$ID.line;
			col= $ID.pos;
			ErrorSem6(linea,col,texto);
		} 
		else if (!sim.esVAR()) 
		{
					texto=$ID.text;
					linea=$ID.line;
					col= $ID.pos;
					ErrorSem7(linea,col,texto);

                } else 
                {
                	sim.setEmprat();
                	eixida = $ID.text;
            	}		
	}
	)
	;
	

NEWline          : ('\r'? '\n'|' '|'\t')+  { skip(); }; 
COMENTARIO       : '/*' .* '*/' {skip();}; 

PARI    :'(' ;
PARD    :')' ;
PYC     :';' ;
ASIG    :'=' ;

MAIN 	:'main';

CLASS   :'class' ;
DOUBLE  :'double';		
INT     :'int' ;
VOID    :'void' ;
LLAVEI  :'{' ;
LLAVED  :'}' ;
ENTERO  :('0'..'9')+ ;
REAL	:('0'..'9')+'.'('0'..'9')+ ;	 
ID      :('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')* ;


